package com.ljlproxy.proxy;

import com.ljlproxy.dao.TicketDao;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.String;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * ClassName：ProxyUtil
 * Description：动态代理工具类
 * Create by Administrator
 * Date：2020/8/21 0021 15:35
 */
public class ProxyUtil {

    /**
     * 动态代理实现
     * 1.创建.java
     * 2.编译.java -> .class
     * 3.反射实例化
     *
     * @return
     */
    public static Object newInstance(Object target) {
        Class<?> targetClass = target.getClass();

        Class<?>[] interfaces = targetClass.getInterfaces();
        Class<?> proxyInterface = interfaces[0];
        String proxyInterfaceName = proxyInterface.getSimpleName();

        String proxyClass = "$Proxy";

        String packageStr = "package com.baidu.proxy;\n";
        String importStr = "import " + proxyInterface.getName() + ";\n";
        String classStr = "public class " + proxyClass + " implements  " + proxyInterfaceName + " {\n";
        String fieldStr = "private " + proxyInterfaceName + " target;\n";
        String constructStr = "public " + proxyClass + "(" + proxyInterfaceName + " target){\n" +
                "this.target=target;\n" +
                "} \n";

        StringBuilder methodStr = new StringBuilder("");
        //获取方法
        Method[] declaredMethods = targetClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            String returnType = declaredMethod.getReturnType().getName();
            String name = declaredMethod.getName();

            StringBuilder methodParamStr = new StringBuilder("( ");
            StringBuilder invokeMethodParamStr = new StringBuilder("( ");
            //遍历参数 构造参数
            Class<?>[] args = declaredMethod.getParameterTypes();
            int paramIndex = 0;
            for (Class<?> arg : args) {
                methodParamStr.append(arg.getName() + " p" + paramIndex + ",");
                invokeMethodParamStr.append("p" + paramIndex + ",");
                paramIndex++;
            }
            methodParamStr.deleteCharAt(methodParamStr.length() - 1);
            invokeMethodParamStr.deleteCharAt(invokeMethodParamStr.length() - 1);

            methodParamStr.append(")");
            invokeMethodParamStr.append(")");


            //有返回值 处理返回值
            if (!"void".equals(returnType)) {
                methodStr.append("public ").append(returnType).append(" ").append(name).append(methodParamStr).append("{\n");
                methodStr.append("System.out.println(\"ljlproxy before\");\n");
                methodStr.append("String result = target." + name + invokeMethodParamStr + ";\n");
                methodStr.append("System.out.println(\"ljlproxy after\");\n");
                methodStr.append(" return result;");
            } else {
                methodStr.append("public ").append(returnType).append(" ").append(name).append(methodParamStr).append("{\n");
                methodStr.append("System.out.println(\"ljlproxy before\");\n");
                methodStr.append("target." + name + invokeMethodParamStr + ";\n");
                methodStr.append("System.out.println(\"ljlproxy after\");\n");
            }

            methodStr.append("}\n");
        }


        String content = packageStr + importStr + classStr + fieldStr + constructStr + methodStr + "}";
//        System.out.println(content);

        //创建java文件
        File file = generateJavaFile(proxyClass, content);

        //编译Java文件
        compilerJavaFile(file);

        return doInstance(proxyClass, proxyInterface, target);
    }


    /**
     * 实例化
     * @param proxyClass
     * @param proxyInterface
     * @param target
     * @return
     */
    private static Object doInstance(String proxyClass, Class<?> proxyInterface, Object target) {
        URL[] urls = new URL[0];
        try {
            urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.baidu.proxy." + proxyClass);

            Constructor constructor = clazz.getConstructor(proxyInterface);
            return constructor.newInstance(target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 编译java文件
     *
     * @param file
     */
    private static void compilerJavaFile(File file) {

        StandardJavaFileManager fileMgr = null;
        try {
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

            fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);

            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
        } finally {
            try {
                fileMgr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 创建java文件
     *
     * @param proxyClass
     */
    private static File generateJavaFile(String proxyClass, String content) {
        String directory = "d:\\com\\baidu\\proxy\\";
        File file = new File(directory + proxyClass + ".java");
        FileWriter fileWriter = null;
        try {
            new File(directory).mkdirs();
            file.createNewFile();

            fileWriter = new FileWriter(file);
            fileWriter.write(content);
            return file;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileWriter.flush();
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}
